//This file contains the mapping middleware.
import 'dart:io';

import 'package:simple_server/src/context.dart';
import 'package:simple_server/src/middleware/middleware.dart';

///RestHandler is a function that handles a request and returns a Future<void>.
typedef RestHandler<T extends Context> = Future<void> Function(T context);
typedef PathHandlerMap<T extends Context> = Map<String, PathHandlerConfig<T>>;
typedef MethodHandlerMap<T extends Context> = Map<String, PathHandlerMap<T>>;

class PathHandlerConfig<T extends Context> {
  final bool auth;
  final RestHandler<T> handler;

  PathHandlerConfig({required this.auth, required this.handler});
}

class MappingMiddleware<T extends Context> extends Middleware<T> {
  final MethodHandlerMap<T> methodHandlerMap = {};

  AuthMiddleware<T>? get authMiddleware {
    return server.getMiddleware<AuthMiddleware<T>>();
  }

  BodyParserMiddleware<T>? get bodyParserMiddleware {
    return server.getMiddleware<BodyParserMiddleware<T>>();
  }

  @override
  Future<void> call(T context, Next next) async {
    final pathHandlerMap = methodHandlerMap[context.method];
    if (pathHandlerMap == null) {
      context.statusCode = HttpStatus.methodNotAllowed;
      return;
    }
    final pathHandlerConfig = pathHandlerMap[context.path];
    if (pathHandlerConfig == null) {
      context.statusCode = HttpStatus.notFound;
      return;
    }
    if (pathHandlerConfig.auth && !context.auth) {
      context.statusCode = HttpStatus.unauthorized;
      return;
    }
    await pathHandlerConfig.handler(context);
  }

  void _addHandler(
    String method,
    String path, {
    required RestHandler<T> handler,
    CheckAuth<T>? checkAuth,
    bool ignoreBodyParser = false,
  }) {
    final pathHandlerMap = methodHandlerMap.putIfAbsent(method, () => {});
    final auth = checkAuth != null;
    if (auth) authMiddleware?.addCheckAuth(path, checkAuth);
    pathHandlerMap[path] = PathHandlerConfig(auth: auth, handler: handler);
    if (ignoreBodyParser) bodyParserMiddleware?.ignoreParseBody(path);
  }

  void get(
    String path, {
    required RestHandler<T> handler,
    CheckAuth<T>? auth,
    bool ignoreBodyParser = false,
  }) {
    _addHandler(
      'get',
      path,
      handler: handler,
      checkAuth: auth,
      ignoreBodyParser: ignoreBodyParser,
    );
  }

  void post(
    String path, {
    required RestHandler<T> handler,
    CheckAuth<T>? auth,
    bool ignoreBodyParser = false,
  }) {
    _addHandler(
      'post',
      path,
      handler: handler,
      checkAuth: auth,
      ignoreBodyParser: ignoreBodyParser,
    );
  }

  void put(
    String path, {
    required RestHandler<T> handler,
    CheckAuth<T>? auth,
    bool ignoreBodyParser = false,
  }) {
    _addHandler(
      'put',
      path,
      handler: handler,
      checkAuth: auth,
      ignoreBodyParser: ignoreBodyParser,
    );
  }

  void delete(
    String path, {
    required RestHandler<T> handler,
    CheckAuth<T>? auth,
    bool ignoreBodyParser = false,
  }) {
    _addHandler(
      'delete',
      path,
      handler: handler,
      checkAuth: auth,
      ignoreBodyParser: ignoreBodyParser,
    );
  }

  void patch(
    String path, {
    required RestHandler<T> handler,
    CheckAuth<T>? auth,
    bool ignoreBodyParser = false,
  }) {
    _addHandler(
      'patch',
      path,
      handler: handler,
      checkAuth: auth,
      ignoreBodyParser: ignoreBodyParser,
    );
  }
}
